削除予定のAWS KMS鍵が利用されたら通知する

削除予定のAWS KMS鍵が利用されたら通知する

Clock Icon2018.08.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

不要になったAWS Key Management Service (AWS KMS) のカスタマーマスターキー(CMK)は無効にしたり、削除することができます。

ここで気をつけないといけないのは、不要と判断した鍵が実は利用されていた場合です。

そこで

  • CloudTrail
  • CloudWatch Logs
  • CloudWatch Alarm

を組み合わせて、削除予定・無効な CMK が利用されたときに、通知する仕組みを作ってみます。

CMK の無効化・削除処理の仕様について

仕様を簡単にまとめます。

無効処理について

CMK は即座に無効・無効解除できます。

無効状態の鍵で Encrypt/Decrypt すると DisabledException エラーが発生します。

$ aws kms encrypt \
  --key-id arn:aws:kms:us-east-1:123456789012:key/XXX \
  --plaintext foo

An error occurred (DisabledException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is disabled.

削除処理について

AWS KMS で作成した CMK を削除するには、7日〜30日の削除保留期間が必ず発生します。 保留期間中はいつでも削除を解除でき、一方で、保留期間がすぎて削除されると、鍵は復元できなくなります。当然、過去に暗号化されたデータは復号できなくなります。

鍵を利用していないことを100%確認が持てないときは、削除ではなく、無効化を検討してください。

削除保留状態の鍵で Encrypt/Decrypt すると KMSInvalidStateException エラーが発生します。

$ aws kms encrypt \
  --key-id arn:aws:kms:us-east-1:123456789012:key/XXX \
  --plaintext foo

An error occurred (KMSInvalidStateException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is pending deletion.

なお、インポートしたキーマテリアルを削除すると、CMK は即座に使用できなくなります。CMK を再び使用するには、同じキーマテリアルを再インポートする必要があります。

参考 https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-delete-key-material.html

CMK のステート

鍵の無効や削除に伴い CMK のステートは次のように推移します。

※鍵をインポートした場合は別途「PendingImport」というステートが存在します。

キーステータスがカスタマーマスターキーの使用に与える影響 も合わせてご確認ください。

有効でないCMKを検知するシステム

次のドキュメントに従い仕組みを作ります。

https://docs.aws.amazon.com/kms/latest/developerguide/deleting-keys-creating-cloudwatch-alarm.html

大まかには、以下の流れで作業します。

  1. CloudWatch Logs への CloudTrail ログファイル配信を設定
  2. エラーログを検出するメトリックフィルタを作成
  3. エラーが検出されたら通知する CloudWatch アラームを作成

検知で取りこぼしがないよう、KMS の API 呼び出しでエラーが発生しているもの全体を通知対象とします。

CloudTrail の有効化

CloudTrail はユーザーアクティビティと API 使用状況の追跡するサービスです。 まだ有効になっていない場合は、次のドキュメントに従い有効化してください。

http://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-and-update-a-trail.html

CloudWatch Logs への CloudTrail ログファイル配信を設定する

CloudTrail から CloudWatch Logs にイベントを送信するよう設定します。

管理コンソールを利用する場合、Trail の Configuration 画面から配信するロググループを指定します。

メトリックフィルターの作成

CloudWatch Logs 画面で配信先に指定したロググループを選択し、"Create Metric Filter" をクリックします。

Define Pattern

Filter Pattern に次のパターンを指定します。

{ $.eventSource = kms.amazonaws.com && ($.errorMessage = "*" || $.errorCode = "*") }

KMS サービスに対する API 呼び出しで、なにか例外が発生したログをフィルタ対象としています。

CloudTrail ログに errorMessage または errorCode の属性が存在する場合、エラーと判定しています。

Assign Metric

  • Filter Name : eventSource-kms-errorCode-Exception
  • Metric Namespace : CloudTrailLogMetrics
  • Metric Name : KMSErrorCount

と入力して、フィルタを作成します。

フィルタ一覧画面にある "Create Alarm" リンクから CloudWatch Alarm の作成画面に移動します。

アラームの作成

先程作成したメトリック(KMSErrorCount)は選択済みです。

このメトリックに対して

Whenever KMSErrorCound >= 1 for 1 out of 1 datapoints

のしきい値でアラームを設定します。

欠落データの扱い("Treat missing data as") は good (not breaching threshold) を選択します。 今回のように、異常時にしかメトリックデータが発生しないようなときに向いています。

詳細はドキュメントをご確認ください。

通知先は Actions で指定します。

個人検証では、メールアドレスに通知する SNS を利用しました。

動作確認

以上で準備が整いました。

CMK を無効・削除して KMS::Encrypt API を呼び出してエラーを発生させ、アラームが機能していることを確認します。

有効状態の CMK の動作確認

$ KEYID=arn:aws:kms:us-east-1:123456789012:key/XXX
$ aws kms describe-key --key-id $KEYID
{
    "KeyMetadata": {
        "KeyManager": "CUSTOMER",
        "KeyId": "XXX",
        "Description": "CMK For S3",
        "CreationDate": 1522329556.173,
        "KeyUsage": "ENCRYPT_DECRYPT",
        "Arn": "arn:aws:kms:us-east-1:123456789012:key/XXX",
        "KeyState": "Enabled",
        "AWSAccountId": "123456789012",
        "Origin": "AWS_KMS",
        "Enabled": true
    }
}
  • Enabled : true
  • KeyState : Enabled

となっています。

この状態で Encrypt を呼び出します

$ aws kms encrypt \
  --key-id $KEYID \
  --plaintext foo
{
    "KeyId": "arn:aws:kms:us-east-1:123456789012:key/XXX",
    "CiphertextBlob": "AQICAHjm+..."
}

成功し、base64 エンコードされた ciphertext を返します。

CMK を無効化して API 呼び出し

次に、この CMK を無効化します。

$ aws kms disable-key --key-id $KEYID
$ aws kms describe-key --key-id $KEYID
{
    "KeyMetadata": {
        "KeyUsage": "ENCRYPT_DECRYPT",
        "AWSAccountId": "123456789012",
        "Arn": "arn:aws:kms:us-east-1:123456789012:key/XXX",
        "Enabled": false,
        "KeyId": "XXX",
        "Description": "CMK For S3",
        "KeyManager": "CUSTOMER",
        "Origin": "AWS_KMS",
        "KeyState": "Disabled",
        "CreationDate": 1522329556.173
    }
}
  • Enabled : true → false
  • KeyState : Enabled → Disabled

に変わっています。

この状態で Encrypt を呼び出します。

$ aws kms encrypt \
  --key-id $KEYID \
  --plaintext foo

An error occurred (DisabledException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is disabled.

DisabledException エラーが発生しました。

CMK を削除してAPI呼び出し

次に、この CMK を削除します。 保留期間(--pending-window-in-days) は10日とします。

$ aws kms schedule-key-deletion --key-id $KEYID \
  --pending-window-in-days 10
{
    "KeyId": "arn:aws:kms:us-east-1:123456789012:key/XXX",
    "DeletionDate": 1534291200.0
}
$ aws kms describe-key --key-id $KEYID
{
    "KeyMetadata": {
        "Enabled": false,
        "KeyId": "XXX",
        "DeletionDate": 1534291200.0,
        "Origin": "AWS_KMS",
        "CreationDate": 1522329556.173,
        "Arn": "arn:aws:kms:us-east-1:123456789012:key/XXX",
        "KeyUsage": "ENCRYPT_DECRYPT",
        "AWSAccountId": "123456789012",
        "KeyManager": "CUSTOMER",
        "Description": "CMK For S3",
        "KeyState": "PendingDeletion"
    }
}
  • Enabled : false
  • DeletionDate : 削除される UNIX TIME
  • KeyState : PendingDeletion

に変わっています。

この状態で Encrypt を呼び出します。

$ aws kms encrypt \
  --key-id $KEYID \
  --plaintext foo

An error occurred (KMSInvalidStateException) when calling the Encrypt operation: arn:aws:kms:us-east-1:123456789012:key/XXX is pending deletion.

KMSInvalidStateException エラーが発生しました。

CloudWatch Alarm の確認

CloudTrail → CloudWatch Logs → CloudWatch Alarm が発火していることを確認します。

管理コンソール

エラー発生件数が記録されており、しきい値を超えているために ALARM ステートになっています。

期待通りです。

メール

SNS 経由で アラームメールが送信されています。

期待どおりです。

CloudTrail ログの確認

CloudTrail での KMS::Encrypt API 呼び出しのログを確認します

メトリックフィルターの条件になっている属性を確認します。

$ aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=Encrypt \
  --max-items 3 |
  jq '.Events[].CloudTrailEvent |
   fromjson |
   {errorCode, errorMessage}'
{
  "errorCode": "KMSInvalidStateException",
  "errorMessage": "arn:aws:kms:us-east-1:123456789012:key/XXX is pending deletion."
}
{
  "errorCode": "DisabledException",
  "errorMessage": "arn:aws:kms:us-east-1:123456789012:key/XXX is disabled."
}
{
  "errorCode": null,
  "errorMessage": null
}

API 呼び出し時と同じです。

参考までに、CloudTrail の CMK 無効化時のログ全体は以下のようになっています。

{
    "eventVersion": "1.05",
    "userIdentity": {
        ...
    },
    "eventTime": "2018-08-04T11:00:07Z",
    "eventSource": "kms.amazonaws.com",
    "eventName": "Encrypt",
    "awsRegion": "us-east-1",
    ...
    "errorCode": "DisabledException",
    "errorMessage": "arn:aws:kms:us-east-1:123456789012:key/XXX is disabled.",
    "requestParameters": {
        "keyId": "arn:aws:kms:us-east-1:123456789012:key/XXX"
    },
    "responseElements": null,
    "requestID": "8c9cbb6e-97d5-11e8-a0a4-0d492c69e0cf",
    "eventID": "63d702bd-52b4-45ad-b72d-88ada860d9f0",
    "readOnly": true,
    "resources": [
        {
            "ARN": "arn:aws:kms:us-east-1:123456789012:key/XXX",
            "accountId": "123456789012",
            "type": "AWS::KMS::Key"
        }
    ],
    "eventType": "AwsApiCall",
    "recipientAccountId": "123456789012"
}

AWS Secrets Manager への応用

2018年4月にはシークレットを管理するマネージドサービス AWS Secrets Manager が発表されました。 このサービスで管理するシークレットは、CMK と同じく、即時に削除状態とはならず、保留期間が設けられています。

今回と同様の仕組みにより、削除予定のシークレットが利用された場合は、検知することができます。

具体的な手順は次のドキュメントを参照ください。

AWS Documentation » AWS Secrets Manager » User Guide » AWS Services That Work with AWS Secrets Manager » Monitor the Use of Your AWS Secrets Manager Secrets

まとめ

無効・削除予定の AWS KMS CMK が利用されたときに、検知する仕組みを紹介しました。

一旦削除されると復元できなくなるため、100%不要であると確信が持てない限り CMK を削除しないようにしてください。

今回はエラー検知の取りこぼしがないように Filter Pattern{ $.eventSource = kms.amazonaws.com && ($.errorMessage = "*" || $.errorCode = "*") } としました。

特定の errorMessage だけを対象にするなど、要件に合わせてカスタマイズしてください。

今回紹介した仕組みは AWS Secrets Manager のシークレットにも活用できます。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.